﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IWMAG.Scripting.Helpers {
    public class SymbolTable<T, S> {
        const int StackLimit = 100;

        public SymbolTable<T, S> Parent { get; set; }

        protected List<Dictionary<T, S>> Data = new List<Dictionary<T, S>>();
        protected string Identifier;

        protected int Top {
            get { return Data.Count - 1; }
        }


        public S this[T key] {
            get {
                return Lookup(key);
            }
            set {
                Set(key, value);
            }
        }

        public SymbolTable(string ident = null) {
            Identifier = ident;
            OpenScope();
        }

        public S Lookup(T key) {
            for (int i = Top; i >= 0; i--) {
                if (Data[i].ContainsKey(key)) {
                    return Data[i][key];
                }
            }

            if (Parent != null) {
                return Parent.Lookup(key);
            }

            throw new KeyNotFoundException(String.Format("Couldn't find {0} in symbol table {1}.", key, Identifier != null ? Identifier : ""));
        }

        public bool Contains(T key) {
            for (int i = Top; i >= 0; i--) {
                if (Data[i].ContainsKey(key)) {
                    return true;
                }
            }

            if (Parent != null) {
                return Parent.Contains(key);
            }

            return false;
        }

        public void OpenScope() {
            if (Data.Count >= StackLimit) {
                throw new StackOverflowException();
            }
            Data.Add(new Dictionary<T, S>());
        }

        public void CloseScope() {
            Data.RemoveAt(Top);
        }

        public void Set(T key, S val) {
            if (!Data[Top].ContainsKey(key)) {
                Data[Top].Add(key, val);
            } else {
                Data[Top][key] = val;
            }
        }

        public void Remove(T key) {
            Data[Top].Remove(key);
        }
    }
}